<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>svg-pie</title>
</head>
<body>
	<div id="svgWrap" class="svg-wrap"></div>
	<script>
        /**
         * 将创建环图的所有操作写在drawPie函数内，配置一些默认参数
         * @param  {Element} element [插入SVG的元素，缺省直接插入到body]
         * @param  {Number} R       [外弧点计算半径]
         * @param  {Number} options.r       [description]
         * @param  {Number} options.width   [description]
         * @param  {Number} options.height  [description]
         * @param  {Array}  options.data    [description]
         * @return {[type]}                 [description]
         */
		function drawPie({element, R = 140, r = 100,width = 450,height = 400,data = []} = {}) {

			let w = width;
            let h = height; //将width、height赋值给w、h
            let origin = [w / 2, h / 2]; //原点坐标
            let drawData = [];
            let sAngel = 0;
            let eAngel = sAngel;
            let cAngel ;
            let leftPoints = []; //保存在左边的点
            let rightPoints= []; //保存在右边的点,分出左右是为了计算两点垂直间距是否靠太近
            let tAngel = Math.PI * 2; 
            let minPadding = 40; //设置数据项两点最小间距
            //total保存总花费,用于计算数据项占比
            let total = data.reduce(function(v, item) {
                return v + item.cost;
            }, 0)

			let svg =  createSvgTag('svg', {
				'width': width + 'px',
				'height': height + 'px',
				'viewBox': `0 0 ${w} ${h}`,		
			});
            drawLine()

            for(let v of data) {
            	let itemData = Object.assign({}, v);
            	let isLeft = false;
            	eAngel = sAngel + v.cost / total * tAngel;
            	itemData.arclineStarts = [evaluateXY(r, sAngel, origin), evaluateXY(R, sAngel, origin) , evaluateXY(R, eAngel, origin), evaluateXY(r, eAngel, origin)] ;
            	itemData.LargeArcFlag = (eAngel - sAngel) > Math.PI ? '1' : '0';
            	itemData.lineStart = evaluateXY(R, sAngel + (eAngel - sAngel)/2, origin);
            	itemData.isLeft = isLeft = itemData.lineStart[0]  < origin[0];
            	itemData.lineEnd = [(isLeft ? 0 : w), itemData.lineStart[1]];
            	itemData.top = itemData.lineStart[1] < origin[1];
            	isLeft? leftPoints.push(itemData) : rightPoints.push(itemData);
            	drawData.push(itemData)
            	sAngel = eAngel;
            }

           makeUseable(rightPoints); //处理右边挤在一起的情况
           makeUseable(leftPoints.reverse(), true); //处理左边挤在一起的情况

            for(let [i, v] of drawData.entries()) {
            	let arcP = v.arclineStarts;
                if (i>0) break ;
            	let path = createSvgTag('path', {
					'fill': 'none',
					'stroke': v.color,
					'stroke-width': '0.5',
					'd': `M ${arcP[0][0]} ${arcP[0][1]} L ${arcP[1][0]} ${arcP[1][1]} A ${R} ${R} 0 ${v.LargeArcFlag} 1 ${arcP[2][0]} ${arcP[2][1]} L ${arcP[3][0]} ${arcP[3][1]} A ${r} ${r}  0 ${v.LargeArcFlag} 0 ${arcP[0][0]} ${arcP[0][1]} z`
				})
				let linePoints = v.lineStart[0] + ' ' + v.lineStart[1];
				v.turingPoints && (linePoints += ',' + v.turingPoints[0] + ' ' + v.turingPoints[1])
				linePoints += ',' + v.lineEnd[0] + ' ' + v.lineEnd[1];

                for (let [i,v] of arcP.entries()) {
                    let cost = createSvgTag("text", {
                        'x':  v[0],
                        'y':  v[1],
                        'dy': -2,
                        style: `fill:black;font-size:12px;text-anchor: ${v.isLeft? 'start':'end'};`
                    })
                    cost.innerHTML = `P${i}`;
                    svg.appendChild(cost);
                }
				let polyline = createSvgTag('polyline', {
					points: linePoints,
					style: `fill:none;stroke:${v.color};stroke-width:.5`
				})
				let cost = createSvgTag("text", {
					'x':  v.lineEnd[0],
					'y':  v.lineEnd[1],
					'dy': -2,
					style: `fill:${v.color};font-size:12px;text-anchor: ${v.isLeft? 'start':'end'};`
				})
				cost.innerHTML = v.cost;
				let category = createSvgTag("text", {
					'x':  v.lineEnd[0],
					'y':  v.lineEnd[1],
					'dy': 14,
					style: `fill:${v.color};font-size:12px;text-anchor: ${v.isLeft? 'start':'end'};`
				})
				category.innerHTML = v.category;
				svg.appendChild(path);				
				//svg.appendChild(polyline);
				//svg.appendChild(cost);
				//svg.appendChild(category);
            } 

			

            (element && element.appendChild) ? element.appendChild(svg) : document.body.appendChild(svg);
			
			return svg;


			function evaluateXY (r, angel, origin) {
				return [origin[0] + Math.sin(angel) * r, origin[1] - Math.cos(angel) * r]                                                                                  
			}
			function createSvgTag (tagName, attributes) {
				let tag = document.createElementNS('http://www.w3.org/2000/svg', tagName)
				for (let attr in attributes) {
					tag.setAttribute(attr, attributes[attr])
				}
				return tag;
			}

			function isUseable(arr) {
                if (arr.length <= 1)
                    return true;

                return arr.every(function(p, index, arr) {
                    if (index === arr.length-1) {
                        return true;
                    } else {
                        return arr[index + 1].lineEnd[1]  - p.lineEnd[1] >= minPadding;
                    }
                })
            }

            function makeUseable(arr, left) {
                let diff, turingAngel, x, maths = Math.sin,diffH, l;

                while (!isUseable(arr)) { 

                    for (let i = 0, len = arr.length - 1; i < len; i++) { 

                        diff = arr[i + 1].lineEnd[1] - arr[i].lineEnd[1]; 

                        if (diff < minPadding) { 

                            if (arr[i].top && arr[i + 1].top) { 

                                if (arr[i].lineEnd[1] - (minPadding - diff) > 0 && arr[i].lineEnd[1] > i * minPadding) {
                                    arr[i].lineEnd[1] = arr[i].lineEnd[1] - (minPadding - diff);
                                } else {
                                    arr[i].lineEnd[1] = i * minPadding;
                                    arr[i + 1].lineEnd[1] = arr[i + 1].lineEnd[1] + (minPadding - diff);
                                }

                            } else {
                                if (arr[i + 1].lineEnd[1] + (minPadding - diff) < h && arr[i + 1].lineEnd[1] < h - (len - i - 1) * minPadding) {
                                    arr[i + 1].lineEnd[1] = arr[i + 1].lineEnd[1] + (minPadding - diff)
                                } else {
                                    arr[i + 1].lineEnd[1] = h - (len - i - 1) * minPadding;
                                    arr[i].lineEnd[1] = arr[i].lineEnd[1] - (minPadding - diff);
                                }
                            }

                            break; 
                        }
                    }
                }



                for (let i = 0, len = arr.length; i < len; i++) { 
                    if (arr[i].lineStart[1] !== arr[i].lineEnd[1]) { 
                        arr[i].turingPoints = [arr[i].lineEnd[0] + (left ? 40 : -40) , arr[i].lineEnd[1]];
                    }
                }
            }


            function drawLine() {
            svg.appendChild(createSvgTag('line', {
                x1:origin[0],
                y1:0,
                x2:origin[0],
                y2:h,
                style:'stroke:black;stroke-width:0.5;'
            }))
            svg.appendChild(createSvgTag('line', {
                x1:origin[0],
                y1:0,
                x2:origin[0]-5,
                y2:5,
                style:'stroke:black;stroke-width:0.5;'
            }))
            svg.appendChild(createSvgTag('line', {
                x1:origin[0],
                y1:0,
                x2:origin[0]+5,
                y2:5,
                style:'stroke:black;stroke-width:0.5;'
            }))
            svg.appendChild(createSvgTag('line', {
                x1:0,
                y1:origin[1],
                x2:w,
                y2:origin[1],
                style:'stroke:black;stroke-width:0.5;'
            }))
            svg.appendChild(createSvgTag('line', {
                x1:w,
                y1:origin[1],
                x2:w-5,
                y2:origin[1]-5,
                style:'stroke:black;stroke-width:0.5;'
            }))
            svg.appendChild(createSvgTag('line', {
                x1:w,
                y1:origin[1],
                x2:w-5,
                y2:origin[1]+5,
                style:'stroke:black;stroke-width:0.5;'
            }))                      
             let cost = createSvgTag("text", {
                'x':  origin[0],
                'y':  origin[1],
                'dy': 15,
                'dx':5,
                style: `fill:black;font-size:12px;text-anchor:  'start';`
            })             
            cost.innerHTML = `O:[${origin[0]}, ${origin[1]}]`;
            svg.appendChild(cost)

            let [x2, y2] = evaluateXY(R, 0.1*Math.PI, origin);
            svg.appendChild(createSvgTag('line', {
                x1:origin[0],
                y1:origin[1],
                x2,
                y2,
                style:'stroke:black;stroke-width:0.5;'
            })) 
            cost = createSvgTag("text", {
                'x':  origin[0] + 10,
                'y':  origin[1] - 70,
                style: `fill:black;font-size:12px;text-anchor:  'start';`
            })             
            cost.innerHTML = `R`;
            svg.appendChild(cost)

            let [x4, y4] = evaluateXY(r, 0.2*Math.PI, origin);
            svg.appendChild(createSvgTag('line', {
                x1:origin[0],
                y1:origin[1],
                x2:x4,
                y2:y4,
                style:'stroke:black;stroke-width:0.5;'
            })) 
            cost = createSvgTag("text", {
                'x':  origin[0] + 30,
                'y':  origin[1] - 50,
                style: `fill:black;font-size:14px;text-anchor:  'start';`
            })             
            cost.innerHTML = `r`;
            svg.appendChild(cost)               
            }
		}
		drawPie({
			element: document.getElementById('svgWrap'),
			data: [{
                cost: 16.94,
                category: '通讯',
                color: "#e95e45",
            }, {
                cost: 4.78,
                category: '服装美容',
                color: "#20b6ab",
            }, {
                cost: 4.00,
                category: '交通出行',
                color: "#ef7340",
            }, {
                cost: 3.00,
                category: '饮食',
                color: "#eeb328",
            }, {
                cost: 49.40,
                category: '其他',
                color: "#f79954",
            }, {
                cost: 28.77,
                category: '生活日用',
                color: "#00a294",
            }]
		})
	</script>
</body>
</html>